package com.sdses.weather.config.aop;

import com.github.benmanes.caffeine.cache.Cache;
import com.sdses.weather.config.cache.CaffeineCacheManager;
import com.sdses.weather.config.cache.DynamicCacheable;
import com.sdses.weather.config.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Method;

@Aspect
@Component
@Slf4j
public class DynamicCacheAspect {

    @Autowired
    private CaffeineCacheManager cacheManager;

    private final ExpressionParser parser = new SpelExpressionParser();
    private final LocalVariableTableParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();

    @Around("@annotation(dynamicCacheable)")
    public Object around(ProceedingJoinPoint joinPoint, DynamicCacheable dynamicCacheable) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        String[] paramNames = paramNameDiscoverer.getParameterNames(method);
        Object[] args = joinPoint.getArgs();

        // 构建 SpEL 上下文
        EvaluationContext context = new StandardEvaluationContext();
        if (paramNames != null) {
            for (int i = 0; i < paramNames.length; i++) {
                context.setVariable(paramNames[i], args[i]);
            }
        }

        //目前方法使用不到
        context.setVariable("args", args);
        for (int i = 0; i < args.length; i++) {
            context.setVariable("p" + i, args[i]);
            context.setVariable("a" + i, args[i]);
        }

        // 计算缓存 key
        Expression keyExpr = parser.parseExpression(dynamicCacheable.key());
        String key = keyExpr.getValue(context, String.class);
        if (!StringUtils.hasText(key)) {
            throw new IllegalArgumentException("Cache key cannot be empty");
        }

        // 获取缓存实例
        Cache<Object, Object> cache = cacheManager.getCache(dynamicCacheable.cacheName());
        // ======== 核心修改：使用 cache.get(key, Callable) 支持 sync ========
        try {
            Object result = cache.get(key, k -> {
                try {
                    // 只有第一个线程会执行 proceed()
                    Object originResult = joinPoint.proceed();

                    // 解析 TTL
                    int ttlSeconds = parseTtlSeconds(context, dynamicCacheable);

                    // 包装并返回（注意：这里不能 put，因为 get 已经 put 了）
                    return new CaffeineCacheManager.TimedValue(originResult, ttlSeconds);
                } catch (Throwable t) {
                    if (t instanceof BusinessException) {
                        throw (BusinessException) t;
                    }
                    if (t instanceof Exception) {
                        try {
                            throw (Exception) t;
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    } else {
                        throw new IllegalStateException(t);
                    }
                }
            });

            // unwrap TimedValue
            if (result instanceof CaffeineCacheManager.TimedValue) {
                return ((CaffeineCacheManager.TimedValue) result).getValue();
            }
            return result;

        } catch (Exception ex) {
            throw ex;
        } catch (Throwable t) {
            throw new IllegalStateException(t);
        }
    }

    // 解析 TTL 的私有方法
    private int parseTtlSeconds(EvaluationContext context, DynamicCacheable dynamicCacheable) {
        int ttlSeconds = 300;
        String ttlExpression = dynamicCacheable.ttl();
        if (StringUtils.hasText(ttlExpression)) {
            try {
                Expression ttlExpr = parser.parseExpression(ttlExpression);
                Object ttlValue = ttlExpr.getValue(context, Object.class);
                if (ttlValue != null) {
                    ttlSeconds = Integer.parseInt(ttlValue.toString());
                }
            } catch (Exception e) {
                log.error("解析时间异常 {}", ttlExpression, e);
            }
        }
        return ttlSeconds;
    }
}